package com.gzhu.knowledgeAdmin.service.impl;

import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gzhu.knowledgeAdmin.common.constant.RoleType;
import com.gzhu.knowledgeAdmin.common.handler.ScenceImportHandler;
import com.gzhu.knowledgeAdmin.common.handler.ZSImportHandler;
import com.gzhu.knowledgeAdmin.common.utils.UserInfoUtils;
import com.gzhu.knowledgeAdmin.common.vo.*;
import com.gzhu.knowledgeAdmin.dao.ZsESDao;
import com.gzhu.knowledgeAdmin.entity.*;
import com.gzhu.knowledgeAdmin.service.*;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;


import com.gzhu.knowledgeAdmin.dao.ZsDao;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;




@Service("zsService")
public class ZsServiceImpl extends ServiceImpl<ZsDao, Zs> implements ZsService {

    @Autowired
    private ZsflService zsflService;


    @Autowired
    private XtyhscService xtyhscService;


    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ExamineService examineService;

    @Autowired
    private ElasticsearchRestTemplate esTemplate;

    @Autowired
    private ZsESDao zsESDao;

    @Autowired
    private ExecutorService executorService; //线程池

    @Override
    public PageVo queryPage(Map<String, Object> params) {

        Page<KnowledgeVo> knowledgeVoPage = getKnowledgeVoPage(params,false);
        return new PageVo(knowledgeVoPage);
    }

    @Override
    @Transactional
    public void effective(List<Integer> ids) {

        Date date = new Date();
        Timestamp timestamp = new Timestamp(date.getTime());

        UpdateWrapper<Zs> wrapper = new UpdateWrapper<>();
        // 未审核的知识才能生效
        wrapper.in("F_ID",ids).eq("F_SFSHENX","0").set("F_SHENXSJ",timestamp).set("F_SFSHENX",1);

        // 非系统管理员只能审核自己公司的知识
        User userInfo = UserInfoUtils.getCurrentUser();

        assert userInfo != null;
        if(userInfo.getYhlx()==RoleType.ADMIN){
            wrapper.eq("F_GSID",userInfo.getGsid());
        }


        baseMapper.update(new Zs(),wrapper);

        // 更新审核表，记录审核员
        examineService.setExamineUser(userInfo.getUserId(),ids);

    }




    @Override
    @Transactional
    public void saveZs(KnowledgeAddOrUpdateVo knowledgeAdd) {

        Zs zs = new Zs();
        BeanUtils.copyProperties(knowledgeAdd,zs);

        // 设置一级分类id、末级分类id
        List<Integer> zslxIds = knowledgeAdd.getZslxIds();

        zs.setZsflid(zslxIds.get(zslxIds.size()-1));
        zs.setZsfjflid(zslxIds.get(0));

        // 设置发布者信息
        User curUser = UserInfoUtils.getCurrentUser();

        zs.setFbzid(curUser.getUserId());

        // 未生效
        zs.setSfshenx("0");
        // 公司的员工发布的知识就是属于自己公司
        zs.setGsid(curUser.getGsid());


        baseMapper.insert(zs);


        // 在审核表记录
        Examine examine = new Examine();
        examine.setZsId(zs.getId());
        examineService.save(examine);

        // 同步到ES
        ZsESVo zsESVo = new ZsESVo();
        convertToZsESVo(zs,zsESVo );
        zsESDao.save(zsESVo);
    }

    @Override
    public void removeZsByIds(List<Integer> ids) {
        // 设置失效时间则代表知识下线
        Date date = new Date();
        Timestamp timestamp = new Timestamp(date.getTime());

        UpdateWrapper<Zs> wrapper = new UpdateWrapper<>();

        // 以上线的知识才能下线
        wrapper.in("F_ID", ids).eq("F_SFSHENX","1").set("F_SHIXSJ", timestamp).set("F_SFSHENX","2");

        // 普通管理员只能下线本公司的
        User curUser = UserInfoUtils.getCurrentUser();
        if(curUser.getYhlx()==RoleType.ADMIN){
            wrapper.eq("F_GSID",curUser.getGsid());
        }

        update(new Zs(), wrapper);
    }

    @Override
    public List<OneLevel> getAllOneLevel() {
        return zsflService.getAlloneLevel();
    }



    @Override
    public KnowledgeResponseVo getZsById(Integer id) {
        KnowledgeResponseVo knowledgeResponseVo = new KnowledgeResponseVo();

        // 查询到记录并封装返回结果
        Zs zs = baseMapper.selectById(id);
        BeanUtils.copyProperties(zs,knowledgeResponseVo);

        // 查询该知识所属分类的所有分类id
        List<Integer> lxIds = zsflService.getAllFlIds(zs);
        knowledgeResponseVo.setZslxIds(lxIds);


        return knowledgeResponseVo;
    }

    @Override
    public KnowledgeDetailVo getZsDetail(Integer id) {

        KnowledgeDetailVo knowledgeDetailVo = baseMapper.selectDetailById(id);

        return knowledgeDetailVo;
    }

    /**
     * 根据gsId将知识缓存到redis中
     * key：问题
     * value: 回答数组
     * @param gsId 公司id
     */
    @Override
    public void cacheZsByGsId(String gsId) {
        StringBuilder stringBuilder = new StringBuilder();
        Map<String,String> map = new HashMap<>();
        // 从数据库中查询出来
        List<KnowledgeQAVo> zsList = baseMapper.selectQAList(gsId);
        for (KnowledgeQAVo knowledgeQAVo : zsList) {

            // 拼接回答数组str
            stringBuilder.append(knowledgeQAVo.getHdnr());
            if(!StringUtils.isEmpty(knowledgeQAVo.getHdtyj())){
                stringBuilder.append('|');
                stringBuilder.append(knowledgeQAVo.getHdtyj());
            }
            String hdStr = stringBuilder.toString();
            // 清空下次使用
            stringBuilder.delete(0,stringBuilder.length());

            // 问题
            map.put(gsId+":"+knowledgeQAVo.getWtnr(),hdStr);

            // 问题同义句
            if(!StringUtils.isEmpty(knowledgeQAVo.getWttyj())){
                String[] split = knowledgeQAVo.getWttyj().split("\\|");
                for (String s : split) {
                    map.put(gsId+":"+s,hdStr);
                }
            }


        }


        // 批量添加
        stringRedisTemplate.opsForValue().multiSetIfAbsent(map);

    }

    /**
     * 根据问题，从redis中查找指定公司知识库里的答案
     * @param queryVo
     * @return
     */
    @Override
    public List<String> getAnswerByQuestion(KnowledgeQueryVo queryVo) {
        String key = queryVo.getGsId()+":"+queryVo.getQuestion();
        String answerListStr = stringRedisTemplate.opsForValue().get(key);

        if(StringUtils.isEmpty(answerListStr)){
            return null;
        }
        return Arrays.asList(answerListStr.split("\\|"));
    }

    /**
     * 知识审核未通过
     * @param notPassVo
     */
    @Override
    @Transactional
    public void notPass(NotPassVo notPassVo) {
        // 更新知识状态
        Zs zs = new Zs();
        zs.setId(notPassVo.getZsId());
        zs.setSfshenx("3");
        baseMapper.updateById(zs);

        // 在审核表设置 不通过原因
        examineService.setUnPass(notPassVo);

    }

    /**
     * 获取知识审核不通过详情
     * @param id 知识id
     * @return
     */
    @Override
    public KnowledgeFailDetailVo getZsFailDetail(Integer id) {
        return examineService.getZsFailDetail(id);
    }

    /**
     * 我的知识列表
     * @param params 查询参数
     * @return
     */
    @Override
    public PageVo queryMyKnowledgePage(Map<String, Object> params) {
        Page<KnowledgeVo> knowledgeVoPage = getKnowledgeVoPage(params,true);


        return new PageVo(knowledgeVoPage);

    }

    /**
     * 修改后重新发起审核
     * @param knowledgeAddOrUpdateVo
     */
    @Override
    @Transactional()
    public void updateAndReEamine(KnowledgeAddOrUpdateVo knowledgeAddOrUpdateVo) {
        Zs zs = new Zs();
        BeanUtils.copyProperties(knowledgeAddOrUpdateVo,zs);

        // 设置一级分类和末级分类id
        List<Integer> zslxIds = knowledgeAddOrUpdateVo.getZslxIds();
        if(zslxIds!=null&&zslxIds.size()>0){
            zs.setZsfjflid(zslxIds.get(0));
            zs.setZsflid(zslxIds.get(zslxIds.size()-1));
        }

        // 修改知识
        zs.setSfshenx("0");// 状态改为审核中
        updateById(zs);

        // 重新审核
        examineService.reExamine(zs.getId());

        // 同步到ES
        ZsESVo zsESVo = new ZsESVo();
        convertToZsESVo(zs,zsESVo );
        zsESDao.save(zsESVo);

    }

    private Page<KnowledgeVo> getKnowledgeVoPage(Map<String, Object> params,boolean isPersonal) {
        long pageNo = 1;
        long pageSize = 10;
        // 获取当前页和每页大小
        String pageNoStr = (String) params.get("page");
        String pageSizeStr = (String) params.get("limit");
        String keyWord = (String) params.get("keyWord");
        String status = (String) params.get("status");
        String zsflId = (String) params.get("zsflId");

        if (!StringUtils.isEmpty(pageNoStr)){
            pageNo = Long.parseLong(pageNoStr);
        }
        if (!StringUtils.isEmpty(pageSizeStr)){
            pageSize = Long.parseLong(pageSizeStr);
        }

        Page<KnowledgeVo> page = new Page<>(pageNo, pageSize);
        QueryWrapper<KnowledgeVo> wrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(keyWord)) {

            // 根据params里的查询参数添加条件查询

            wrapper.and(queryWrapper->queryWrapper.like("t1.F_ZSBT", keyWord)
                    .or().like("t3.F_FLMC", keyWord)
                    .or().like("t1.F_WTNR", keyWord)
                    .or().like("t1.F_WTTYJ", keyWord));
        }

        // 指定分类
        if(!StringUtils.isEmpty(zsflId)){
            wrapper.eq("t1.F_ZSFLID",Integer.parseInt(zsflId));
        }

        // 生效情况
        if(!StringUtils.isEmpty(status)){
            switch (status) {
                case "0":
                    // 未生效
                    wrapper.eq("t1.F_SFSHENX", "0").isNull("t1.F_SHIXSJ");
                    break;
                case "1":
                    // 已生效
                    wrapper.eq("t1.F_SFSHENX", "1");
                    break;
                case "2":
                    // 已失效
                    wrapper.eq("t1.F_SFSHENX","2");
                    break;
                case "3":
                    // 审核未通过
                    wrapper.eq("t1.F_SFSHENX","3");
                    break;
            }
        }


        // 普通管理员和员工只能查询自己公司的知识
        User user = UserInfoUtils.getCurrentUser();

        // 如果是查询个人的知识
        if(isPersonal){
            wrapper.eq("t1.F_FBZID",user.getUserId());
        }else {
            // 查询自己公司的知识
                wrapper.eq("t1.F_GSID",user.getGsid());
        }

        page = baseMapper.page(page,wrapper);


        List<KnowledgeVo> collect = page.getRecords();
        // 查询公司所有知识需要设置收藏情况
        if(!isPersonal){
            collect = collect.stream().peek(item -> {
                int count = xtyhscService.count(new QueryWrapper<Xtyhsc>().eq("F_ZSID", item.getId()).eq("F_YHID", user.getUserId()));
                item.setCollected(count > 0);
            }).collect(Collectors.toList());
        }


        // 重新封装page对象
        Page<KnowledgeVo> knowledgeVoPage = new Page<>();
        knowledgeVoPage.setRecords(collect);
        knowledgeVoPage.setTotal(page.getTotal());
        knowledgeVoPage.setSize(page.getSize());
        knowledgeVoPage.setCurrent(page.getCurrent());
        knowledgeVoPage.setPages(page.getPages());
        return knowledgeVoPage;
    }

    @Override
    public PageVo search(Map<String, Object> params) {


        int pageNumber = 1;
        int pageSize = 10;
        // 获取当前页和每页大小
        String pageNoStr = (String) params.get("page");
        String pageSizeStr = (String) params.get("limit");
        String keyword = (String) params.get("keyWord");

        if (!StringUtils.isEmpty(pageNoStr)){
            pageNumber = Integer.parseInt(pageNoStr);
        }
        if (!StringUtils.isEmpty(pageSizeStr)){
            pageSize = Integer.parseInt(pageSizeStr);
        }


        // 获取当前公司id
        String gsid = UserInfoUtils.getCurrentUser().getGsid();

        // 创建查询构建器
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if(StringUtils.hasLength(keyword)){
            boolQueryBuilder.must(QueryBuilders.multiMatchQuery(keyword,"wtList","hdList","wtnr","hdnr","zsbt"));
        }
        boolQueryBuilder.must(QueryBuilders.termQuery("gsid",gsid));

        // 创建高亮构建器
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("wtnr");
        highlightBuilder.field("hdnr");
        highlightBuilder.field("wtList");
        highlightBuilder.field("hdList");
        highlightBuilder.field("zsbt");
        highlightBuilder.preTags("<span style=\"background-color: aqua;\">");
        highlightBuilder.postTags("</span>");




        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withHighlightBuilder(highlightBuilder).withQuery(boolQueryBuilder);
        // 分页
        nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNumber - 1, pageSize));
        NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();


        // 查询
        SearchHits<ZsESVo> search = esTemplate.search(searchQuery, ZsESVo.class);


        List<SearchHit<ZsESVo>> searchHits = search.getSearchHits();
        ArrayList<ZsSearchResultVo> zsSearchResultVos = new ArrayList<>();

        // 处理查询结果并替换高亮内容
        for (SearchHit<ZsESVo> hit : searchHits) {
            ZsESVo entity = hit.getContent();
            ZsSearchResultVo zsSearchResultVo = new ZsSearchResultVo();

            // 抽取高亮显示的内容，拼接到一起
            StringBuilder stringBuilder = new StringBuilder();

            if(!hit.getHighlightFields().isEmpty()){
                // 替换questionSentence字段的高亮内容
                List<String> highlightField = hit.getHighlightFields().get("wtnr");
                if(highlightField!=null&& !highlightField.isEmpty()){
                    entity.setWtnr(highlightField.get(0));
                    stringBuilder.append(" <strong>问题</strong>: "+highlightField.get(0));
                }

                List<String> highlightField1 = hit.getHighlightFields().get("hdnr");
                if(highlightField1!=null&&!highlightField1.isEmpty()){
                    entity.setHdnr(highlightField1.get(0));
                    stringBuilder.append(" <strong>回答</strong>: "+highlightField1.get(0));
                }

                List<String> highlightField2 = hit.getHighlightFields().get("wtList");
                if(highlightField2!=null&&!highlightField2.isEmpty()){
                    entity.setHdList(highlightField2);
                    // 将字符串数组转化为字符串，用|分割
                    StringBuilder builder = new StringBuilder();
                    for (String s : highlightField2) {
                        builder.append(s);
                        builder.append("|");
                    }
                    builder.deleteCharAt(builder.length()-1);

                    stringBuilder.append(" <strong>问题同义句</strong>："+ builder);
                }
                List<String> highlightField3 = hit.getHighlightFields().get("hdList");
                if(highlightField3!=null&&!highlightField3.isEmpty()){
                    entity.setHdList(highlightField3);
                    // 将字符串数组转化为字符串，用|分割
                    StringBuilder builder = new StringBuilder();
                    for (String s : highlightField3) {
                        builder.append(s);
                        builder.append("|");
                    }
                    builder.deleteCharAt(builder.length()-1);
                    stringBuilder.append(" <strong>回答同义句</strong>："+builder);
                }

                List<String> highlightField4 = hit.getHighlightFields().get("zsbt");
                if(highlightField4!=null&&!highlightField4.isEmpty()){
                    entity.setZsbt(highlightField4.get(0));
                    stringBuilder.append(" <strong>回答同义句</strong>："+highlightField4);
                }

                String s = stringBuilder.toString();

                // 属性复制
                BeanUtils.copyProperties(entity,zsSearchResultVo);
                zsSearchResultVo.setHighlightContent(s);

            }

            zsSearchResultVos.add(zsSearchResultVo);
        }

        // 封装返回结果
        PageVo pageVo = new PageVo();
        pageVo.setList(zsSearchResultVos);
        pageVo.setCurrPage(pageNumber);
        pageVo.setPageSize(pageSize);
        pageVo.setTotalPage((int) (search.getTotalHits()%pageSize));
        pageVo.setTotalCount((int) search.getTotalHits());

        return pageVo;

    }

    private void convertToZsESVo(Zs zs, ZsESVo zsESVo) {
        BeanUtils.copyProperties(zs,zsESVo);

        // 设置同义句
        String wttyj = zs.getWttyj();
        if(StringUtils.hasLength(wttyj)){
            String[] split = wttyj.split("\\|");
            List<String> collect = Arrays.stream(split).collect(Collectors.toList());
            zsESVo.setWtList(collect);
        }

        String hdtyj = zs.getHdtyj();
        if(StringUtils.hasLength(hdtyj)){
            String[] split = hdtyj.split("\\|");
            List<String> collect = Arrays.stream(split).collect(Collectors.toList());
            zsESVo.setHdList(collect);
        }
    }


    /**
     * 导入全部数据到es
     */
//    @Scheduled(cron = "0 0 3 * * ?")
    @Override
    public void importAllToES() throws InterruptedException {

        // 清空es数据
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchAllQuery())
                .build();
        esTemplate.delete(searchQuery,ZsESVo.class);

        // 清空es数据
//        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
//                .withQuery(QueryBuilders.matchAllQuery())
//                .build();
//        esTemplate.delete(searchQuery,ZsESVo.class);

        //查询数据总数
        int count = getTotalCount();

        int PAGE_SIZE = 10;
        //总页数用 数据库数据总数%每页记录数
        int totalPageSize = count % PAGE_SIZE == 0 ? count / PAGE_SIZE : count / PAGE_SIZE + 1;

        //一共有多少页，就创建多少个CountDownLatch的计数
        CountDownLatch countDownLatch = new CountDownLatch(totalPageSize);

        int fromIndex;
        for (int i = 0; i < totalPageSize; i++) {
            //起始分页条数
            fromIndex = i * PAGE_SIZE;
            //查询数据库当前页数的数据
            List<ZsESVo> zsESVoList = getCurrentData(fromIndex,PAGE_SIZE);

            //创建线程，做批量插入es数据操作
            TaskThread taskThread = new TaskThread(zsESVoList, countDownLatch);
            //把当前线程任务交由线程池执行
            executorService.execute(taskThread);
        }
        //调用await()方法,用来等待计数归零
        countDownLatch.await();

    }

    @Override
    public void updateZs(KnowledgeAddOrUpdateVo knowledgeAddOrUpdateVo) {
        Zs zs = new Zs();

        BeanUtils.copyProperties(knowledgeAddOrUpdateVo,zs);
        // 设置一级分类和末级分类id
        List<Integer> zslxIds = knowledgeAddOrUpdateVo.getZslxIds();
        if(zslxIds!=null&&zslxIds.size()>0){
            zs.setZsfjflid(zslxIds.get(0));
            zs.setZsflid(zslxIds.get(zslxIds.size()-1));
        }
        updateById(zs);

        // 获取最新的zs记录
        zs = getById(zs.getId());

        // 同步到ES
        ZsESVo zsESVo = new ZsESVo();
        convertToZsESVo(zs,zsESVo );
        zsESDao.save(zsESVo);

    }

    @Override
    public String importZS(MultipartFile file) throws IOException {
        ExcelReader reader = ExcelUtil.getReader(file.getInputStream());

        List<ZSImportVo> zsList = reader.readAll(ZSImportVo.class);

        ZSImportHandler zsImportHandler = new ZSImportHandler(zsList);
        String msg = zsImportHandler.handle();
        return msg;
    }

    private List<ZsESVo> getCurrentData(int fromIndex,int pageSize) {

        List<Zs> zsList = baseMapper.selectCurrentData(fromIndex,pageSize );
        List<ZsESVo> zsESVoList = new ArrayList<>();
        for (Zs zs : zsList) {
            ZsESVo zsESVo = new ZsESVo();
            convertToZsESVo(zs,zsESVo);

            zsESVoList.add(zsESVo);
        }

        return zsESVoList;
    }


    private int getTotalCount() {

        Integer count = baseMapper.selectCount(new QueryWrapper<>());
        return count;
    }

    class TaskThread implements Runnable {
        List<ZsESVo> zsESVoList;
        CountDownLatch cdl;
        //数据和倒计时锁
        public TaskThread(List<ZsESVo> zsESVoList, CountDownLatch cdl) {
            this.zsESVoList = zsESVoList;
            this.cdl = cdl;
        }
        @Override
        public void run() {
            //创建ES对象,并指定名称
            esTemplate.save(zsESVoList);
            //添加成功后计数减一
            cdl.countDown();
        }
    }




}